package abc.generator;

import abc.base.BaseGenerator;
import abc.base.SchemaInfo;
import abc.config.DatasourceConfigure;
import abc.config.MybatisPlusAutoFillConfig;
import abc.utils.JacksonUtil;
import abc.utils.StringUtil;
import abc.utils.YamlUtil;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Slf4j
@Component
public class AbcGenerator extends BaseGenerator {


    protected AtomicReference<String> packageName = new AtomicReference<>("");
    private boolean isSetDirectoryForTemplateLoading;

    public AbcGenerator() {
        super();
    }

    public AbcGenerator(DatasourceConfigure datasourceConfigure, MybatisPlusAutoFillConfig mybatisPlusAutoFillConfig) {
        super(datasourceConfigure, mybatisPlusAutoFillConfig);
    }

    public AbcGenerator(DatasourceConfigure datasourceConfigure, MybatisPlusAutoFillConfig mybatisPlusAutoFillConfig,
                        String projectName, Boolean isSupportNacos, String context, List<String> excludeFields,
                        List<String> logicDeleteField, List<String> versionField, List<String> ignoreAssociative,
                        String apiInputParameterType, Boolean swagger, String application) {

        super(datasourceConfigure, mybatisPlusAutoFillConfig);
        this.projectName = projectName;
        this.application = application;
        this.isSupportNacos = isSupportNacos;
        this.context = context;
        this.swagger = swagger;
        this.ignoreAssociative = ignoreAssociative;
        this.apiInputParameterType = apiInputParameterType;
        if(excludeFields == null) {
            excludeFields = new ArrayList<>();
        }
        this.excludeFields = excludeFields;

        if(logicDeleteField == null) {
            logicDeleteField = new ArrayList<>();
        }
        this.logicDeleteField = logicDeleteField;

        if(versionField == null) {
            versionField = new ArrayList<>();
        }
        this.versionField = versionField;
    }

    private List<String> ignoreAssociative;

    private String apiInputParameterType;

    private boolean toVo = false;

    private String projectName = "";

    private String application = "service-abc";

    private Boolean isSupportNacos = false;

    private String context = "";

    private Boolean swagger = false;

    private List<String> excludeFields;

    private List<String> logicDeleteField;

    private List<String> versionField;

    private List<SchemaInfo> controllersPackagePathList = new ArrayList<>();

    /**
     * 生成Api代码
     */
    @Override
    protected void build(DataSourceConfig.Builder datasourceConfigBuilder, Map<String, Map<String, Object>> tableMap, List<Map<String, Object>> columns, String datasourceName, String dbname) throws Exception {
        FastAutoGenerator.create(datasourceConfigBuilder).strategyConfig((scanner, builder) -> {
            if(StringUtil.isEmpty(projectName) || StringUtil.isEmpty(projectName.trim())) {
                String root = scanner.apply("请输入项目名称生成根目录：");
                while (StringUtil.isEmpty(root) || new File(System.getProperty("user.dir") + File.separator + root).exists()) {
                    if (StringUtil.isEmpty(root)) {
                        continue;
                    }
                    root = scanner.apply(root + "已存在，请重新输入项目名称生成根目录：");
                }
                projectName = root;
                packageName.set(projectName);
            }
            else {
                packageName.set(projectName);
            }
        })
        .scannerNext(generateFilesForTableMap(datasourceName, dbname, tableMap, columns));
    }

    private String generateFilesForTableMap(String datasourceName, String dbname, Map<String, Map<String, Object>> tableMap, List<Map<String, Object>> columns) throws IOException {
        log.info(String.format("generateFilesForTableMap------------------------------> dbname: %s", dbname));
        var keyList = columns.stream().filter(a-> a.get("keyFlag").toString().equals("1")).collect(Collectors.toList());
        log.info("logicDeleteField: " + JacksonUtil.toJson(logicDeleteField));
        log.info("columns: " + JacksonUtil.toJson(columns));
        columns.forEach(a-> {
            a.put("isAutoIncrement", a.get("isAutoIncrement").toString().equals("1"));
            a.put("keyIdentityFlag", a.get("isAutoIncrement").toString().equals("1"));

            /** keyFlag是boolean型时有问题
            a.put("keyFlag", a.get("keyFlag").toString().equals("1")); **/
            if(keyList.size()>1) {
                a.put("keyFlag", 0);
            }

            a.put("convert", true);
            a.put("propertyName", StringUtil.getHumpName(a.get("propertyName").toString(), true));
            a.put("propertyNameUpper", StringUtil.getHumpName(a.get("propertyName").toString()));
            if(versionField.contains(a.get("versionField").toString())) {
                a.put("versionField", true);
            }
            else {
                a.put("versionField", false);
            }

            if(logicDeleteField.contains(StringUtil.getHumpName(a.get("annotationColumnName").toString(), true))) {
                a.put("logicDeleteField", true);
                log.info("logicDeleteField: " + StringUtil.getHumpName(a.get("annotationColumnName").toString()));
            }
            else {
                a.put("logicDeleteField", false);
            }
            a.put("propertyType", getPropertyType(a.get("dataType").toString()));
        });

        Configuration configuration = new Configuration(Configuration.getVersion());
        configuration.setDefaultEncoding("utf-8");
        URL templateUrl = Thread.currentThread().getContextClassLoader().getResource("templates");
        try {
            String jarPath = "jar:" + templateUrl.getPath();
            // 加载.ftl配置文件所在路径
            if(jarPath.endsWith(".jar!/templates")) {
                System.out.println("templateUrl---------> jar:" + templateUrl.getPath());
                isSetDirectoryForTemplateLoading = false;
            }
            else {
                System.out.println("templateUrl=========> " + templateUrl.getPath());
                configuration.setDirectoryForTemplateLoading(new File(templateUrl.toURI()));
                isSetDirectoryForTemplateLoading = true;
            }


        } catch (Exception exception) {
            exception.printStackTrace();
        }

        /**
         * 查找添加联动字段
         */
        List<Map<String, Object>> colsTemp = columns.stream().filter(a ->
                        a.get("annotationColumnName").toString().matches("^[_\\w][\\w\\d_]*_id$") &&
                                dataExecuor.getTableMap(dbname).keySet().contains(a.get("annotationColumnName").toString()
                                        .replaceAll("^([_\\w][\\w\\d_]*)_id$", "$1")))
                .collect(Collectors.toList());

        for(Map item : colsTemp) {
            Map map = JacksonUtil.convert(item, Map.class);

            map.put("propertyType", String.format("List<%s>", StringUtil.getHumpName(map.get("table").toString()).replaceAll("^([_\\w][\\w\\d_]*)$", "$1To")));
            map.put("propertyName", StringUtil.getHumpName(map.get("table").toString(), true).replaceAll("^([_\\w][\\w\\d_]*)$", "$1List"));
            if(!ignoreAssociative.stream().filter(a-> StringUtil.getHumpName(a).equals(StringUtil.getHumpName(map.get("table").toString()))).findAny().isPresent()) {
                map.put("associative", String.format("@Associative(table=\"%s\", foreignKey=\"%s\", type=%sTo.class)", map.get("table"), item.get("annotationColumnName"), StringUtil.getHumpName(map.get("table").toString())));
            }
            map.put("table", item.get("annotationColumnName").toString().replaceAll("^([_\\w][\\w\\d_]*)_id$", "$1"));
            map.put("propertyNameUpper", StringUtil.getHumpName(map.get("propertyName").toString()));
            columns.add(map);
        }


        /**
         * 遍历生成代码
         */
        tableMap.entrySet().forEach(table-> {
            List<Map<String, Object>> filterColumns = columns.stream()
                    .filter(a -> table.getKey().equals(a.get("table")))
                    .collect(Collectors.toList());

            log.info("begin generate code.......................");
            generateCode(configuration, datasourceName, dbname, table, "controller", filterColumns, "abc-api", false);
            generateCode(configuration, datasourceName, dbname, table, "service", filterColumns, "abc-api", false);
            generateCode(configuration, datasourceName, dbname, table, "service/impl", filterColumns, "abc-api", false);
            generateCode(configuration, datasourceName, dbname, table, "mapper", filterColumns, "abc-api", false);
            generateCode(configuration, datasourceName, dbname, table, "mapper/xml", filterColumns, "abc-api", false);
            generateCode(configuration, datasourceName, dbname, table, "model", filterColumns, "abc-business", true);
            generateCode(configuration, datasourceName, dbname, table, "vo", filterColumns, "abc-clients", false);
            generateCode(configuration, datasourceName, dbname, table, "client", filterColumns, "abc-clients", false);
            generateCode(configuration, datasourceName, dbname, table, "bll", filterColumns, "abc-business", false);
            generateCode(configuration, datasourceName, dbname, table, "to", filterColumns, "abc-clients", true);

            generateConfigCode(configuration, "abc-api", "pom", "abc-api-pom.xml");
            generateConfigCode(configuration, "abc-business", "pom", "abc-business-pom.xml");
            generateConfigCode(configuration, "abc-clients", "pom", "abc-clients-pom.xml");
            generateApplication(configuration, "abc-api");
            if(this.isSupportNacos) {
//                generateConfigCode(configuration, "abc-api", "config", "NacosConfigProperty.java.ftl");
//                generateConfigCode(configuration, "abc-api", "config", "NacosStarterConfig.java.ftl");

            }
            generateConfigCode(configuration, "abc-api", "resources", "bootstrap.yml");
            generateConfigCode(configuration, "abc-api", "resources", "application.yml");

            if(!isSetDirectoryForTemplateLoading) {
                ensureMainPom();
            }
            generateSpringFactories(configuration, "abc-api");
        });
        System.out.println("-------------------------------" + dbname + "代码生成器执行完毕---------------------------------");
        System.out.println("\n");
        if(runningCount==datasourceConfigure.getDatasource().entrySet().size()) {
            generateConfigCode(configuration, "abc-api", "config", "SpringDocConfig.java");
            System.exit(0);
        }
        return "按任意键并回车继续>>>>>>>>>>>>>>>>>>>>>>>>>>>>>";
    }

    private void ensureMainPom() {
        MavenXpp3Reader reader = new MavenXpp3Reader();
        MavenXpp3Writer writer = new MavenXpp3Writer();
        String mainPom = System.getProperty("user.dir") + File.separator + "pom.xml";

        try {
            Model model = reader.read(new FileReader(mainPom));

            if(model.getParent() == null) {
                model.setParent(new Parent());
            }
            model.getParent().setGroupId("abc-dev");
            model.getParent().setArtifactId("service-abc");
            model.getParent().setVersion("0.0.1-SNAPSHOT");

            if(!"pom".equals(model.getPackaging())) {
                model.setPackaging("pom");
            }

            if(model.getModules() == null) {
                model.setModules(new ArrayList<>());
            }
            if(!model.getModules().contains("abc-clients")) {
                model.getModules().add("abc-clients");
            }
            if(!model.getModules().contains("abc-business")) {
                model.getModules().add("abc-business");
            }
            if(!model.getModules().contains("abc-api")) {
                model.getModules().add("abc-api");
            }
            writer.write(new FileOutputStream(mainPom), model);
        } catch (IOException e) {
            log.error("IOException------------------>");
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            log.error("XmlPullParserException------------------>");
            e.printStackTrace();
        }
    }

    private void generateApplication(Configuration configuration, String root) {
        try {
            // 文件输出路径
            String outputPath = System.getProperty("user.dir") +
                    File.separator + root + File.separator +
                    File.separator + "src" + File.separator +
                    File.separator + "main" + File.separator +
                    File.separator + "java" + File.separator +
                    File.separator + "abc" + File.separator +
                    (StringUtil.isEmpty(packageName.get())? "": File.separator + packageName.get()) +
                    File.separator + "ServiceAbcApplication.java";

            File targetFile = new File(outputPath);
            if(targetFile.exists()) {
                return;
            }
            //给文件赋值
            Template template = getTemplate(configuration, "ServiceAbcApplication.java.ftl");

            if(!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }
            FileOutputStream file = new FileOutputStream(outputPath);
            OutputStreamWriter out = new OutputStreamWriter(file, "utf-8");

            Map<String,Object> params = new HashMap<>();
            params.put("packageName", StringUtil.getHumpName(packageName.get(), true));
            params.put("application", application);
            params.put("context", this.context);
            if(this.isSupportNacos) {
                params.put("supportNacos", this.isSupportNacos);
            }

            template.process(params, out);
            out.close();
        }
        catch (Exception e) {
            log.error("AbcGenerator Error!!! Message: {}, Stack: {}", e.getMessage(), e);
        }
    }

    private void generateSpringFactories(Configuration configuration, String root) {
        try {
            // 文件输出路径
            String outputPath = System.getProperty("user.dir") +
                    File.separator + root + File.separator +
                    File.separator + "src" + File.separator +
                    File.separator + "main" + File.separator +
                    File.separator + "resources" + File.separator +
                    File.separator + "META-INF" + File.separator +
                    "spring.factories";

            File targetFile = new File(outputPath);
            if(targetFile.exists()) {
                return;
            }
            //给文件赋值
            Template template = getTemplate(configuration, "spring.factories.ftl");

            if(!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }
            FileOutputStream file = new FileOutputStream(outputPath);
            OutputStreamWriter out = new OutputStreamWriter(file, "utf-8");

            Map<String,Object> params = new HashMap<>();
            params.put("packageName", StringUtil.getHumpName(packageName.get(), true));
            params.put("context", this.context);
            if(this.isSupportNacos) {
                params.put("supportNacos", this.isSupportNacos);
            }

            template.process(params, out);
            out.close();
        }
        catch (Exception e) {
            log.error("AbcGenerator Error!!! Message: {}, Stack: {}", e.getMessage(), e);
        }
    }

    private void generateConfigCode(Configuration configuration, String root, String dir, String configName) {
        try {
            // 文件输出路径
            String outputPath = System.getProperty("user.dir") +
                    File.separator + root + File.separator +
                    File.separator + "src" + File.separator +
                    File.separator + "main" + File.separator +
                    File.separator + "java" + File.separator +
                    File.separator + "abc" + File.separator +
                    (StringUtil.isEmpty(dir)? "": File.separator + dir + File.separator) +
                    configName;

            if("resources".equals(dir)) {
                outputPath = System.getProperty("user.dir") +
                        File.separator + root + File.separator +
                        File.separator + "src" + File.separator +
                        File.separator + "main" + File.separator +
                        File.separator + "resources" + File.separator +
                        configName;
            }

            if("pom".equals(dir)) {
                outputPath = System.getProperty("user.dir") +
                        File.separator + root + File.separator + "pom.xml";
            }

            File targetFile = new File(outputPath);
            if(targetFile.exists()) {
                return;
            }

            if(!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }

            //给文件赋值
            Template template = getTemplate(configuration, String.format("%s.ftl", configName));

            FileOutputStream file = new FileOutputStream(outputPath);
            OutputStreamWriter out = new OutputStreamWriter(file, "utf-8");

            Map<String,Object> params = new HashMap<>();
            params.put("workerId", "${random.int(1,31)}");
            params.put("datacenterId", "${random.int(1,31)}");
            params.put("context", this.context);
            params.put("java17", "${docker.base.java17.image}");
            params.put("dockerRegistry", "${docker.registry}");
            params.put("dockeRegistryNamespace", "${docker.registry.namespace}");
            params.put("artifactId", "${project.artifactId}");
            params.put("projectVersion", "${project.version}");
            params.put("dockerUsername", "${docker.username}");
            params.put("dockerPassword", "${docker.password}");
            params.put("springProfilesActive", "${spring.profiles.active:dev}");
            params.put("serviceName", "${spring.application.name:service-abc}");
            params.put("springCloudNacosDiscoveryGoup", "${spring.cloud.nacos.discovery.group:DEFAULT_GROUP}");
            params.put("springCloudNacosDiscoveryNamespace", "${spring.cloud.nacos.discovery.namespace:public}");
            params.put("springCloudNacosDiscoveryRegisterEnabled", "${spring.cloud.nacos.discovery.register-enabled:false}");
            params.put("springCloudNacosDiscoveryServerAddr", "${spring.cloud.nacos.discovery.server-addr:}");
            params.put("springCloudNacosConfigServerAddr", "${spring.cloud.nacos.config.server-addr:}");
            params.put("springCloudNacosDiscoveryIp", "${spring.cloud.nacos.discovery.ip:}");
            params.put("springCloudNacosDiscoveryPort", "${spring.cloud.nacos.discovery.port:}");
            params.put("serverPort", "${server.port:0}");
            params.put("serverPorts", "${server.ports:}");
            params.put("springCloudNacosConfigSharedConfigs", "${spring.cloud.nacos.config.shared-configs:}");
            params.put("yamlNacosConfigSharedConfigs", "${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}");
            params.put("packageName", packageName.get());
            params.put("swaggerPackages", controllersPackagePathList);
            params.put("application", application);
            if(this.isSupportNacos) {
                params.put("supportNacos", this.isSupportNacos);
            }

            if("application.yml".equals(configName)) {
                String key = "spring.datasource.dynamic.datasource";
                List<String> keys = StringUtil.splitString2List(key, "\\.");
                String content = YamlUtil.getText(key, keys.size()*2, "");
                params.put("datasourceContent", content);
                Map<String, Object> datasourceMap = YamlUtil.getMap(key);
                if(datasourceMap.size()>0) {
                    var first = datasourceMap.entrySet().stream().map(a-> a.getKey()).findFirst();
                    if(first.isPresent()) {
                        params.put("primaryDatasource", first.get());
                    }
                }
            }

            template.process(params, out);
            out.close();

            File applicationFille = new File(System.getProperty("user.dir") + File.separator + "application.yml");
            if(applicationFille.exists()) {
                applicationFille.delete();
            }
        }
        catch (Exception e) {
            log.error("AbcGenerator Error!!! Message: {}, Stack: {}", e.getMessage(), e);
        }
    }

    private void generateCode(Configuration configuration, String datasourceName, String dbname, Map.Entry<String, Map<String, Object>> table, String templatePath, List<Map<String, Object>> columns, String root, boolean isOverride) {
        try {
            if(StringUtil.isEmpty(templatePath)) {
                return;
            }
            if(!"mapper/xml".equals(templatePath)) {
                columns = columns.stream()
                        .filter(a -> !"id".equals(a.get("annotationColumnName")) &&
                                !this.excludeFields.contains(a.get("annotationColumnName")))
                        .collect(Collectors.toList());
            }
            boolean isXml = false;
            List<String> templateList = StringUtil.splitString2List(templatePath, "/");
            String templateName = StringUtil.join("", templateList.stream().map(a-> StringUtil.getHumpName(a)).collect(Collectors.toList()));
            if(templateList.size()==2 && "xml".equals(templateList.get(1))) {
                templateName = templateList.get(0);
                isXml = true;
            }
            templateName = templateName.substring(0, 1).toLowerCase(Locale.ROOT) + templateName.substring(1);
            Map<String, String> packageConfigMap = new HashMap<>();
            packageConfigMap.put(StringUtil.getHumpName(templateName), String.format("abc.%s.%s.%s", packageName.get(), datasourceName, templateName));
            packageConfigMap.put("To", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "to"));
            packageConfigMap.put("Model", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "model"));
            packageConfigMap.put("Entity", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "model"));
            packageConfigMap.put("Bll", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "bll"));
            packageConfigMap.put("Service", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "service"));
            packageConfigMap.put("ServiceImpl", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "service.impl"));
            packageConfigMap.put("Mapper", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "mapper"));
            packageConfigMap.put("Controller", String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "controller"));
            if(!controllersPackagePathList.stream()
                    .filter(a-> datasourceName.equals(a.getGroupName()))
                    .findAny().isPresent()) {
                controllersPackagePathList.add(new SchemaInfo() {{
                    setGroupName(datasourceName);
                    setPackagesToScan(String.format("abc.%s.%s.%s", packageName.get(), datasourceName, "controller"));
                    setPathsToMatch(String.format("/%s/%s/**", packageName.get(), datasourceName));
                }});
            }
            packageConfigMap.put("ModuleName", packageName.get());

            table.getValue().put("name", table.getKey());
            table.getValue().put("convert", true);

            if("vo".equals(templateName)) {
                toVo = true;
            }

            Boolean hasAssociative = false;
            List<Map<String, Object>> currentColumns = new ArrayList<>();
            if("to".equals(templateName) || "model".equals(templateName)) {
                currentColumns = columns;
                hasAssociative = true;
            }
            else {
                currentColumns = columns.stream().filter(a -> a.get("associative") == null).collect(Collectors.toList());
            }

            currentColumns.forEach(map -> {
                map.put("convert", true);
                if(autoFillConfig.getAutoFillProperty() == null) {
                    return;
                }
                if(autoFillConfig.getAutoFillProperty().getInsertUpdate()!=null && autoFillConfig.getAutoFillProperty().getInsertUpdate().contains(StringUtil.getHumpName(map.get("propertyName").toString(), true))) {
                    map.put("fill", "INSERT_UPDATE");
                }
                else if(autoFillConfig.getAutoFillProperty().getInsert()!=null && autoFillConfig.getAutoFillProperty().getInsert().contains(StringUtil.getHumpName(map.get("propertyName").toString(), true))) {
                    map.put("fill", "INSERT");
                }
                else if(autoFillConfig.getAutoFillProperty().getUpdate()!=null && autoFillConfig.getAutoFillProperty().getUpdate().contains(StringUtil.getHumpName(map.get("propertyName").toString(), true))) {
                    map.put("fill", "UPDATE");
                }
            });
            table.getValue().put("fields", currentColumns);
            table.getValue().put("comment", table.getValue().get("table_comment"));
            table.getValue().put(String.format("%sName", templateName), String.format("%s%s", StringUtil.getHumpName(table.getKey()), StringUtil.getHumpName(templateName)));
            table.getValue().put("entityPath", StringUtil.getHumpName(table.getKey(), true));
            table.getValue().put("commonFields", new ArrayList<>());
            String finalTemplateName = templateName;
            table.getValue().put("importPackages", new ArrayList<String>() {{
                add("java.util.Date");
                add("lombok.Data");
                if("vo".equals(finalTemplateName) || "to".equals(finalTemplateName)) {
                    if("to".equals(finalTemplateName)) {
                        add("abc.base.BaseEntity");
                    }
                }
                else {
                    add("com.baomidou.mybatisplus.annotation.TableField");
                    add("com.baomidou.mybatisplus.annotation.TableId");
                    add("com.baomidou.mybatisplus.annotation.TableName");
                    add("java.io.Serializable");
                    add(String.format("abc.base.Base%s", StringUtil.getHumpName(finalTemplateName)));
                }
                if("vo".equals(finalTemplateName)) {
                    add(String.format("abc.%s.%s.to.%sTo", packageName.get(), datasourceName, StringUtil.getHumpName(table.getKey())));
                }
            }});
            table.getValue().put("mapperName", String.format("%sDao", StringUtil.getHumpName(table.getKey())));
            table.getValue().put("serviceName", String.format("%sService", StringUtil.getHumpName(table.getKey())));
            table.getValue().put("serviceImplName", String.format("%sServiceImpl", StringUtil.getHumpName(table.getKey())));

            // 放模板变量的值
            Map<String,Object> params = new HashMap<>();
            var keyPropertyNameOpt = columns.stream().filter(a-> a.get("keyFlag").toString().equals("1")).map(b-> b.get("propertyName").toString()).findAny();
            if(keyPropertyNameOpt.isPresent()) {
                params.put("keyPropertyName", keyPropertyNameOpt.get());
            }
            params.put("context", this.context);
            params.put("package", packageConfigMap);
            params.put("restControllerStyle", true);
            params.put("application", application);
            params.put("author","linlurui");
            params.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
            params.put("comments",String.format("%s 控制器", table.getValue().get("table_comment")));
            params.put("table", table.getValue());
            params.put("controllerMappingHyphenStyle", false);
            params.put("kotlin", false);
            params.put("swagger", swagger);
            params.put("chainModel", true);
            params.put("cacheClassName", false);
            params.put("entityLombokModel", true);
            params.put("baseResultMap", true);
            params.put("baseColumnList", false);
            params.put("toVo", toVo);
            params.put("schemaName", "");
            params.put("entityClass", StringUtil.getHumpName(table.getKey()));
            params.put("bll", String.format("%sBll", StringUtil.getHumpName(table.getKey())));
            params.put("model", String.format("%sModel", StringUtil.getHumpName(table.getKey())));
            params.put("entity", String.format("%sModel", StringUtil.getHumpName(table.getKey())));
            params.put("to", String.format("%sTo", StringUtil.getHumpName(table.getKey())));
            params.put("vo", String.format("%sVo", StringUtil.getHumpName(table.getKey())));
            params.put("activeRecord", true);
            params.put("entitySerialVersionUID", true);
            params.put("entityColumnConstant", false);
            params.put("mapperAnnotationClass", new HashMap<String, String>() {{
                put("name", "org.apache.ibatis.annotations.*");
                put("simpleName", "Mapper");
            }});
            params.put("implementsIEntity", String.format("implements IEntity<%s>", params.get("model")));

            if(swagger) {
                params.put("superControllerClassPackage", "abc.base.AbcBaseController");
                if (StringUtil.isEmpty(apiInputParameterType) || "to".equals(apiInputParameterType)) {
                    params.put("superControllerClass", String.format("AbcBaseController<%s, %s>", params.get("to"), params.get("vo")));
                } else {
                    params.put("superControllerClass", String.format("AbcBaseController<%s, %s>", apiInputParameterType, params.get("vo")));
                }
            }
            else {
                params.put("superControllerClassPackage", "abc.base.BaseController");
                params.put("superControllerClass", "BaseController<HashMap>");
            }
            params.put("superModelClass", "BaseModel");
            params.put("superEntityClassPackage", "abc.base.BaseModel");
            params.put("superEntityClass", "BaseModel");
            params.put("superToClassPackage", "abc.base.BaseEntity");
            params.put("superToClass", "BaseEntity");
            params.put("superVoClassPackage", String.format("abc.base.%s", params.get("to")));
            params.put("superVoClass", params.get("to"));
            params.put("superMapperClassPackage", "abc.base.BaseMapper");
            params.put("superMapperClass", "BaseMapper");
            params.put("superServiceClassPackage", "com.baomidou.mybatisplus.extension.service.IService");
            params.put("superServiceClass", "IService");
            params.put("superServiceImplClassPackage", "com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
            params.put("superServiceImplClass", "ServiceImpl");
            params.put("getprefix", "");
            params.put("enableCache", false);
            //params.put("idType", "ASSIGN_ID");
            params.put("dbname", dbname);
            params.put("dsname", datasourceName);
            params.put("dsnameController", datasourceName.replace("_", "/"));
            params.put("dsnamePackage", datasourceName.replace("_", "."));
            params.put("ResponseData", String.format("ResponseData<List<%s>>", params.get("vo")));
            params.put("hasAssociative", hasAssociative);

            if(this.isSupportNacos) {
                params.put("supportNacos", this.isSupportNacos);
            }

            // 文件输出路径
            String outputPath = System.getProperty("user.dir") +
                    File.separator + root + File.separator +
                    File.separator + "src" + File.separator +
                    File.separator + "main" + File.separator +
                    File.separator + "java" + File.separator +
                    File.separator + "abc" + File.separator +
                    (StringUtil.isEmpty(packageName.get())? "": File.separator + packageName.get().toLowerCase() + File.separator) +
                    getSubPath(templatePath, templateName, datasourceName) +
                    (isXml? (StringUtil.getHumpName(table.getKey())+"Mapper.xml"): (table.getValue().get(String.format("%sName", templateName)) + ".java"));

            if("model".equals(templateName)) {
                templateName = "entity";
            }

            File targetFile = new File(outputPath);
            if(!isOverride && targetFile.exists()) {
                return;
            }

            //给文件赋值
            Template template = getTemplate(configuration, isXml ? String.format("%s.xml.ftl", templateName) : String.format("%s.java.ftl", templateName));

            if(!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }
            FileOutputStream file = new FileOutputStream(outputPath);
            OutputStreamWriter out = new OutputStreamWriter(file, "utf-8");
            template.process(params, out);
            out.close();
        }
        catch (Exception e) {
            log.error("AbcGenerator Error!!! Message: {}, Stack: {}", e.getMessage(), e);
        }
    }

    private Template getTemplate(Configuration configuration, String filePath) throws IOException {
        if(isSetDirectoryForTemplateLoading) {
            return configuration.getTemplate(filePath);
        }
        System.out.println("getResourceAsStream------> templates/" + filePath);
        //获取文件内容
        InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("templates/" + filePath);
        String content = new BufferedReader(new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8))
                .lines()
                .collect(Collectors.joining("\n"));

        //将其填充到stringTemplateLoader中
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate(filePath, content);
        configuration.setTemplateLoader(stringTemplateLoader);

        return configuration.getTemplate(filePath);
    }

    private String getSubPath(String templatePath, String templateName, String dbname) {
        if("service/impl".equals(templatePath)) {
            return File.separator + dbname + File.separator +
                    File.separator + "service" + File.separator +
                    File.separator + "impl" + File.separator;
        }
        else if("mapper/xml".equals(templatePath)) {
            return File.separator + dbname + File.separator +
                    File.separator + "mapper" + File.separator +
                    File.separator + "xml" + File.separator;
        }
        else {
            return File.separator + dbname + File.separator +
                    File.separator + templatePath + File.separator;
        }
    }

    private Object getPropertyType(String dataType) {
        dataType = dataType.toLowerCase(Locale.ROOT);
        switch (dataType) {
            case "smallint":
            case "tinyint":
            case "int":
                return "Integer";
            case "bigint":
            case "timestamp":
                return "Long";
            case "decimal":
                return "BigDecimal";
            case "datetime":
            case "date":
            case "time":
                return "Date";
            case "bit":
                return "Boolean";
            case "binary":
            case "blob":
                return "ByteArray";
        }
        return "String";
    }
}
